package cn.sxgan.gen.utils;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * 文件流处理工具类
 * 功能：将本地文件通过 HttpServletResponse 返回给前端
 */
public class FileUtils {

    /**
     * 返回文件流给前端（自动识别下载/预览模式）
     * 
     * @param response HttpServletResponse 对象
     * @param filePath 文件绝对路径
     * @param isDownload 是否强制下载（true=下载，false=根据类型自动判断）
     * @throws IOException 文件不存在或流处理异常
     */
    public static void writeFileToResponse(HttpServletResponse response,
                                           String filePath,
                                           boolean isDownload) throws IOException {
        File file = new File(filePath);
        
        // 1. 校验文件状态
        if (!file.exists()) {
            throw new IOException("File not found: " + filePath);
        }
        if (!file.isFile()) {
            throw new IOException("Path is not a file: " + filePath);
        }

        // 2. 设置响应头
        String fileName = file.getName();
        String contentType = isDownload ? "application/octet-stream" 
                                       : getContentTypeByFileName(fileName);
        response.setContentType(contentType);
        
        // 3. 处理文件名编码（解决中文乱码）
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
                .replace("+", "%20");
        String disposition = isDownload ? "attachment" : "inline";
        response.setHeader("Content-Disposition", 
                          disposition + "; filename=\"" + encodedFileName + "\"");
        
        // 4. 设置文件长度（避免大文件传输中断）
        response.setContentLengthLong(file.length());

        // 5. 使用缓冲流提升读写性能
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
             OutputStream os = response.getOutputStream()) {
            
            // 6. 流复制（避免 OOM）
            StreamUtils.copy(bis, os);
            os.flush();
        } // try-with-resources 自动关闭流
    }

    /**
     * 根据文件名自动识别 Content-Type
     * 
     * @param fileName 文件名
     * @return MIME 类型
     */
    private static String getContentTypeByFileName(String fileName) {
        String extension = StringUtils.getFilenameExtension(fileName).toLowerCase();
        switch (extension) {
            case "pdf": return "application/pdf";
            case "jpg": case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            case "txt": return "text/plain";
            case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            default: return "application/octet-stream";
        }
    }
    
    /**
     * 通用删除方法：支持删除文件或文件夹（包括非空目录）
     * @param target 要删除的文件或目录
     * @return 是否成功删除
     */
    public static boolean deleteFileOrDirectory(File target) {
        if (target == null || !target.exists()) {
            return false; // 文件不存在直接返回[6,8](@ref)
        }
        
        // 如果是目录，递归删除内容
        if (target.isDirectory()) {
            return deleteDirectoryRecursively(target);
        } else {
            return deleteSingleFile(target);
        }
    }
    
    // 删除单个文件（含重试机制）
    private static boolean deleteSingleFile(File file) {
        int maxAttempts = 3;
        for (int i = 0; i < maxAttempts; i++) {
            if (file.delete()) {
                return true;
            }
            try {
                Thread.sleep(100); // 等待100ms后重试
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return false;
    }
    
    // 递归删除目录（兼容深层次嵌套结构）
    private static boolean deleteDirectoryRecursively(File dir) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectoryRecursively(file); // 递归删除子目录[2,3](@ref)
                } else {
                    deleteSingleFile(file); // 删除文件
                }
            }
        }
        return dir.delete(); // 删除空目录
    }
    
    /* ---------- 进阶方案：使用 Java NIO（推荐，性能更强） ---------- */
    /**
     * 使用 NIO 删除文件/目录（Java 7+）
     * @param target 要删除的文件或目录
     * @throws IOException 删除失败时抛出异常
     */
    public static void deleteWithNIO(File target) throws IOException {
        Path path = target.toPath();
        if (!Files.exists(path)) return;
        
        // 遍历并删除所有子项
        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file); // 删除文件[5](@ref)
                return FileVisitResult.CONTINUE;
            }
            
            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                Files.delete(dir); // 删除空目录[5](@ref)
                return FileVisitResult.CONTINUE;
            }
        });
    }
}